//============================================================================
// Copyright (C) 2010 Brett R. Jones
// All Rights Reserved
//
// You may redistribute and/or modify for non commercial and commercial uses
// provided this copyright notice remains in place and is not modified
//
// This code is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
//
// brettjones1900@gmail.com
// http://www.p2panarchy.com
//============================================================================
#include "DtwGame.h"

//------------------------------------------------------------------------------
DtwGameMgr::DtwGameMgr( const char * externStoragePath )
	: m_bIsRunning(0)			// if true then is loaded and running or paused
	, m_bIsPaused(0)			// if true then game is paused
	, m_bShutdownRequested(0)	// if true we need to begin clean up and exit
	, m_bIsShuttingDown(0)		// if true we are in the process of clean up and exit
	, m_poCurState(0)
	, m_poNewState(0)
	, m_u32LastAppTimeMs(0)
	, m_bInitialized(false)
{
	m_strAssetsRootPath = externStoragePath;

	// set path to assets for irrlicht
	SetGameAssetPath( m_strAssetsRootPath.c_str() );

	GameInputBase::m_poIrrBase = this;
}

//------------------------------------------------------------------------------
DtwGameMgr::~DtwGameMgr()
{
}

//------------------------------------------------------------------------------
//! startup
RCODE DtwGameMgr::Startup(	HWND			hWnd,					// handle to window.. if null then create window of size specified
							int				iScreenWidth,			// window width in pixels
							int				iScreenHeight,			// window height in pixels
							const char *	pConfigFile )			// configuration file if any
{
	log_msg( 0, "DtwGameMgr::Startup Stage 1\n" );

	// initialize database etc
	RCODE rc = g_SaveImp.Startup( m_strAssetsRootPath.c_str() );
	if( rc )
	{
		log_msg( 0, "DtwGameMgr::Startup: SaveImp::Instance()->Startup ERROR %d\n", rc );
		return rc;
	}
	// initialize engine config so we have physics settings
	irr::core::stringc strEngineConfigFile = GetAssetPathAndName( "engine_config.xml", eGameAssetTypeSave );
	rc = g_EngineConfigImp.Startup( strEngineConfigFile.c_str() );
	if( rc )
	{
		log_msg( 0, "DtwGameMgr::Startup: EngineConfigImp::Instance()->Startup ERROR %d\n", rc );
		return rc;
	}
	// initialize user config so we have video and audio settings
	irr::core::stringc strConfigFile = GetAssetPathAndName( "user_config.xml", eGameAssetTypeSave );
	rc = g_UserConfigImp.Startup( strConfigFile.c_str() );
	if( rc )
	{
		log_msg( 0, "DtwGameMgr::Startup: g_UserConfigImp->Startup ERROR %d\n", rc );
		return rc;
	}
	log_msg( 0, "DtwGameMgr::Startup Stage 2\n");
	// fire up audio with enable setting from config
	rc = g_oSndMgr.Startup( g_UserConfigImp.m_bAudioEnabled );
	if( rc )
	{
		log_msg( 0, "DtwGameMgr::Startup: g_oSndMgr.Startup ERROR %d\n", rc );
		return rc;
	}

	// initialize high scores
	irr::core::stringc strHighScoresFile = GetAssetPathAndName( "highscores.xml", eGameAssetTypeSave );
	rc = g_HighScoresImp.Startup( strHighScoresFile.c_str() );
	if( rc )
	{
		log_msg( 0, "DtwGameMgr::Startup: g_HighScoresImp->Startup ERROR %d\n", rc );
		return rc;
	}

	log_msg( 0, "DtwGameMgr::Startup Stage 3\n");
	// fire up irrlicht
	#ifdef WIN_PLATFORM
		rc = IrrBaseStartup( hWnd, iScreenWidth, iScreenHeight );
	#else
		rc = IrrBaseStartup( iScreenWidth, iScreenHeight );
	#endif
	if( rc )
	{
		log_msg( 0, "DtwGameMgr::Startup: IrrBaseStartup ERROR %d\n", rc );
		return rc;
	}
	// set irrlicht event reciever to our event reciever 
	m_poDevice->setEventReceiver( this );
	log_msg( 0, "DtwGameMgr::Startup Stage 4\n");

	// initialize physics
	this->m_poPhysBase = new DtwPhysics();
	this->m_poPhysBase->PhysStartup( this );

	// startup manager of entities
	rc = g_EntMgrImp.Startup( this );
	if( rc )
	{
		log_msg( 0, "DtwGameMgr::Startup: g_EntMgrImp.Startup ERROR %d\n", rc );
		return rc;
	}

	// load level and high score stats 
	rc = g_SaveImp.LoadLevelStatsFromDb();
	if( rc )
	{
		log_msg( 0, "DtwGameMgr::Startup: SaveImp::Instance().LoadLevelStatsFromDb ERROR %d\n", rc );
		return rc;
	}

	log_msg( 0, "DtwGameMgr::Startup: asset dir %s\n", GetGameAssetPath().c_str() );

	// Set the first state
	m_poCurState = new DtwStateMenu();
	m_poNewState = NULL;
	m_eStateStep = eGameStateStepStartup;

	m_bIsRunning = true;
	m_bIsShuttingDown	= false;
	m_bShutdownRequested = false;
	log_msg( 0, "DtwGameMgr::Startup Complete\n");
	m_bInitialized = true;
	return 0;
}

//------------------------------------------------------------------------------
//! shutdown
void DtwGameMgr::Shutdown( void )
{
	log_msg( 0, "DtwGameMgr::Shutdown: \n" );
	m_bIsRunning		= false;
	m_bIsShuttingDown	= true;
	if( m_poCurState )
	{
//		m_poCurState->GameStateShutdown();
		delete m_poCurState;
		m_poCurState = NULL;
	}

	if( m_poNewState )
	{
		delete m_poNewState;
		m_poNewState = NULL;
	}

	log_msg( 0, "DtwGameMgr::Shutdown: EntMgrImp shutdown\n" );
	g_EntMgrImp.Shutdown();
	log_msg( 0, "DtwGameMgr::Shutdown: Scripting shutdown\n" );
	g_ScriptingImp.Shutdown();
	log_msg( 0, "DtwGameMgr::Shutdown: IrrBase  shutdown\n" );
	IrrBaseShutdown();
	log_msg( 0, "DtwGameMgr::Shutdown: User Config  shutdown\n" );
	g_UserConfigImp.Shutdown();
	log_msg( 0, "DtwGameMgr::Shutdown: Engine Config  shutdown\n" );
	g_EngineConfigImp.Shutdown();
	log_msg( 0, "DtwGameMgr::Shutdown: Save  shutdown\n" );
	g_SaveImp.Shutdown();
	log_msg( 0, "DtwGameMgr::Shutdown: Save  shutdown\n" );
	g_oSndMgr.Shutdown();
}

//------------------------------------------------------------------------------
//! resize view screen
void DtwGameMgr::ResizeView( int iScreenWidth, int iScreenHeight )
{
	if( false == m_bInitialized )
	{
		Startup( NULL, iScreenWidth, iScreenHeight, NULL );
	}
}

//------------------------------------------------------------------------------
//! called after processed HandleOrientationEvent for derived classes to override
bool DtwGameMgr::OnOrientationEvent( F32 f32RotX, F32 f32RotY, F32 f32RotZ  )
{
	if( false == m_bIsRunning )
	{
		return false;
	}
	if( m_poCurState )
	{
		return m_poCurState->OnOrientationEvent( f32RotX, f32RotY, f32RotZ  );
	}
	return false;
}

//------------------------------------------------------------------------------
//! called after processed HandleMouseEvent for derived classes to override
bool DtwGameMgr::OnMouseEvent( EMouseButtonType eMouseButType, EMouseEventType eMouseEventType, int iMouseXPos, int iMouseYPos  )
{
	if( false == m_bIsRunning )
	{
		return false;
	}
	//log_msg( 0, "DtwGameMgr::OnMouseEvent: m_poCurState %d, EMouseButtonType %d, eMouseEventType %d, X=%d, Y=%d\n",
	//	m_poCurState, eMouseButType, eMouseEventType, iMouseXPos, iMouseYPos );
	if( m_poCurState )
	{
		return m_poCurState->OnMouseEvent( eMouseButType, eMouseEventType, iMouseXPos, iMouseYPos  );
	}
	return false;
}

//------------------------------------------------------------------------------
//! called after processed HandleMouseWheel for derived classes to override
bool DtwGameMgr::OnMouseWheel( F32 f32MouseWheelDist )
{
	if( false == m_bIsRunning )
	{
		return false;
	}
	if( m_poCurState )
	{
		return m_poCurState->OnMouseWheel( f32MouseWheelDist );
	}
	return false;
}

//------------------------------------------------------------------------------
//! called after processed HandleKeyEvent for derived classes to override
bool DtwGameMgr::OnKeyEvent( EKeyEventType eKeyEventType, irr::EKEY_CODE eKey, int iFlags )
{
	if( false == m_bIsRunning )
	{
		return false;
	}
	if( m_poCurState )
	{
		return m_poCurState->OnKeyEvent( eKeyEventType, eKey, iFlags );
	}
	return false;
}

//------------------------------------------------------------------------------
//! called after processed HandleGuiEvent for derived classes to override
bool DtwGameMgr::OnGuiEvent( irr::gui::EGUI_EVENT_TYPE eGuiEventType, irr::gui::IGUIElement * poGuiElement )
{
	if( false == m_bIsRunning )
	{
		return false;
	}
	if( m_poCurState )
	{
		return m_poCurState->OnGuiEvent( eGuiEventType, poGuiElement );
	}
	return false;
}

//------------------------------------------------------------------------------
//! called after processed HandleKeyEvent
bool DtwGameMgr::OnInputAction( EInputActionType eInputActionType, F32 f32PosX, F32 f32PosY, F32 f32PosZ  )
{
	if( false == m_bIsRunning )
	{
		return false;
	}
	if( m_poCurState )
	{
		return m_poCurState->OnInputAction( eInputActionType, f32PosX, f32PosY, f32PosZ );
	}
	return false;
}

//------------------------------------------------------------------------------
//! update game.. u32TimeMs is time in millisec since app start
RCODE DtwGameMgr::UpdateGame( U32 u32AppTimeMs )
{
	if( m_bShutdownRequested )
	{
		m_bIsRunning = false;
		m_bIsShuttingDown = true;
		m_bShutdownRequested = false;
		this->Shutdown();
		return -1;

	}
	if( false == m_bIsRunning )
	{
		return 0;
	}
	// limit frame rate to 60 frames per second
	//if( (u32AppTimeMs - m_u32LastAppTimeMs) < 17 )
	//{
	//	return 0;
	//}
	m_u32LastAppTimeMs = u32AppTimeMs;
	if( m_bIsPaused )
	{
		return 0;
	}

	// if new state has been set then we need to switch to it
	if( m_poNewState )
	{
		// if current state is in update then shutdown before switching to new state
		m_eStateStep = eGameStateStepShutdown;
	}

	// Update the current state
	switch( m_eStateStep ) 
	{
	case eGameStateStepStartup:
		// initialize new state
		//log_msg( 0, "32 Game Instance %d\n", this->m_iGameInstance  );
		if( 0 != m_poCurState->GameStateStartup( this  ) ) 
		{
			// error occurred
			log_msg( LOG_ERROR, "DtwGameMgr::UpdateGame: m_poCurState->GameStateStartup ERROR\n" );
			m_bShutdownRequested = true;
			return 0;
		}
		// next time we do update
		m_eStateStep = eGameStateStepUpdate;
		//log_msg( 0, "33 Game Instance %d\n", m_poIrrBase->m_iGameInstance  );
		break;
	case eGameStateStepUpdate:
		if( m_poCurState->GameStateUpdate( u32AppTimeMs  ) )
		{
			// error occurred
			log_msg( 0, "DtwGameMgr::UpdateGame: m_poCurState->GameStateUpdate ERROR\n" );
			m_bShutdownRequested = true;
			return 0;
		}
		break;
	case eGameStateStepShutdown:
		//log_msg( 0, "34 Game Instance %d\n", m_poIrrBase->m_iGameInstance  );
		m_poCurState->GameStateShutdown();
		delete m_poCurState;
		m_poCurState = m_poNewState; 
		m_poNewState = NULL;
		m_eStateStep = eGameStateStepStartup;
		//log_msg( 0, "35 Game Instance %d\n", m_poIrrBase->m_iGameInstance  );
		break;
	}
	return 0;
}
//------------------------------------------------------------------------------
//! pause game
void DtwGameMgr::OnGamePause( void )
{
	log_msg( 0, "DtwGameMgr::OnGamePause\n");
	SetIsPaused(true);
}
//------------------------------------------------------------------------------
//! resume game
void DtwGameMgr::OnGameResume( void )
{
	log_msg( 0, "DtwGameMgr::OnGameResume\n");
	SetIsPaused(false);
}

//------------------------------------------------------------------------------
//! restart game
void DtwGameMgr::OnGameRestart( void )
{
	log_msg( 0, "DtwGameMgr::OnGameRestart\n");

}
//------------------------------------------------------------------------------
//! restart level
void DtwGameMgr::OnLevelRestart( void )
{
	log_msg( 0, "DtwGameMgr::OnLevelRestart\n");

}


//------------------------------------------------------------------------------
//! called when application goes to sleep
void DtwGameMgr::OnAppSleep( void )
{
}
//------------------------------------------------------------------------------
//! called when application resumes
void DtwGameMgr::OnAppResume( U32 u32FrameTimeMs )
{
	m_u32LastAppTimeMs = u32FrameTimeMs;

	if( 0 != m_poCurState )
	{
		 m_poCurState->GameStateAppResume( u32FrameTimeMs );
	}
}

//------------------------------------------------------------------------------
//! irrlicht was restarted.. let derived class do any required restart actions 
RCODE DtwGameMgr::OnIrrlichtRestart( void )
{
	// set irrlicht event reciever to our event reciever 
	m_poDevice->setEventReceiver( this );
	return 0;
}
//------------------------------------------------------------------------------
//! override VxIrrBase
//! called if collision occures
void DtwGameMgr::HandleCollision(EntBase * poEnt1, EntBase * poEnt2 )
{
}




